package messages
import (
"crypto/rand"
"fmt"
"math"
"math/big"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/asn1tools"
"github.com/jcmturner/gokrb5/v8/config"
"github.com/jcmturner/gokrb5/v8/crypto"
"github.com/jcmturner/gokrb5/v8/iana"
"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
"github.com/jcmturner/gokrb5/v8/iana/flags"
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
"github.com/jcmturner/gokrb5/v8/iana/msgtype"
"github.com/jcmturner/gokrb5/v8/iana/nametype"
"github.com/jcmturner/gokrb5/v8/iana/patype"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/types"
)
type marshalKDCReq struct {
PVNO int `asn1:"explicit,tag:1"`
MsgType int `asn1:"explicit,tag:2"`
PAData types .PADataSequence `asn1:"explicit,optional,tag:3"`
ReqBody asn1 .RawValue `asn1:"explicit,tag:4"`
}
type KDCReqFields struct {
PVNO int
MsgType int
PAData types .PADataSequence
ReqBody KDCReqBody
Renewal bool
}
type ASReq struct {
KDCReqFields
}
type TGSReq struct {
KDCReqFields
}
type marshalKDCReqBody struct {
KDCOptions asn1 .BitString `asn1:"explicit,tag:0"`
CName types .PrincipalName `asn1:"explicit,optional,tag:1"`
Realm string `asn1:"generalstring,explicit,tag:2"`
SName types .PrincipalName `asn1:"explicit,optional,tag:3"`
From time .Time `asn1:"generalized,explicit,optional,tag:4"`
Till time .Time `asn1:"generalized,explicit,tag:5"`
RTime time .Time `asn1:"generalized,explicit,optional,tag:6"`
Nonce int `asn1:"explicit,tag:7"`
EType []int32 `asn1:"explicit,tag:8"`
Addresses []types .HostAddress `asn1:"explicit,optional,tag:9"`
EncAuthData types .EncryptedData `asn1:"explicit,optional,tag:10"`
AdditionalTickets asn1 .RawValue `asn1:"explicit,optional,tag:11"`
}
type KDCReqBody struct {
KDCOptions asn1 .BitString `asn1:"explicit,tag:0"`
CName types .PrincipalName `asn1:"explicit,optional,tag:1"`
Realm string `asn1:"generalstring,explicit,tag:2"`
SName types .PrincipalName `asn1:"explicit,optional,tag:3"`
From time .Time `asn1:"generalized,explicit,optional,tag:4"`
Till time .Time `asn1:"generalized,explicit,tag:5"`
RTime time .Time `asn1:"generalized,explicit,optional,tag:6"`
Nonce int `asn1:"explicit,tag:7"`
EType []int32 `asn1:"explicit,tag:8"`
Addresses []types .HostAddress `asn1:"explicit,optional,tag:9"`
EncAuthData types .EncryptedData `asn1:"explicit,optional,tag:10"`
AdditionalTickets []Ticket `asn1:"explicit,optional,tag:11"`
}
func NewASReqForTGT (realm string , c *config .Config , cname types .PrincipalName ) (ASReq , error ) {
sname := types .PrincipalName {
NameType : nametype .KRB_NT_SRV_INST ,
NameString : []string {"krbtgt" , realm },
}
return NewASReq (realm , c , cname , sname )
}
func NewASReqForChgPasswd (realm string , c *config .Config , cname types .PrincipalName ) (ASReq , error ) {
sname := types .PrincipalName {
NameType : nametype .KRB_NT_PRINCIPAL ,
NameString : []string {"kadmin" , "changepw" },
}
return NewASReq (realm , c , cname , sname )
}
func NewASReq (realm string , c *config .Config , cname , sname types .PrincipalName ) (ASReq , error ) {
nonce , err := rand .Int (rand .Reader , big .NewInt (math .MaxInt32 ))
if err != nil {
return ASReq {}, err
}
t := time .Now ().UTC ()
kopts := types .NewKrbFlags ()
copy (kopts .Bytes , c .LibDefaults .KDCDefaultOptions .Bytes )
kopts .BitLength = c .LibDefaults .KDCDefaultOptions .BitLength
a := ASReq {
KDCReqFields {
PVNO : iana .PVNO ,
MsgType : msgtype .KRB_AS_REQ ,
PAData : types .PADataSequence {},
ReqBody : KDCReqBody {
KDCOptions : kopts ,
Realm : realm ,
CName : cname ,
SName : sname ,
Till : t .Add (c .LibDefaults .TicketLifetime ),
Nonce : int (nonce .Int64 ()),
EType : c .LibDefaults .DefaultTktEnctypeIDs ,
},
},
}
if c .LibDefaults .Forwardable {
types .SetFlag (&a .ReqBody .KDCOptions , flags .Forwardable )
}
if c .LibDefaults .Canonicalize {
types .SetFlag (&a .ReqBody .KDCOptions , flags .Canonicalize )
}
if c .LibDefaults .Proxiable {
types .SetFlag (&a .ReqBody .KDCOptions , flags .Proxiable )
}
if c .LibDefaults .RenewLifetime != 0 {
types .SetFlag (&a .ReqBody .KDCOptions , flags .Renewable )
a .ReqBody .RTime = t .Add (c .LibDefaults .RenewLifetime )
a .ReqBody .RTime = t .Add (time .Duration (48 ) * time .Hour )
}
if !c .LibDefaults .NoAddresses {
ha , err := types .LocalHostAddresses ()
if err != nil {
return a , fmt .Errorf ("could not get local addresses: %v" , err )
}
ha = append (ha , types .HostAddressesFromNetIPs (c .LibDefaults .ExtraAddresses )...)
a .ReqBody .Addresses = ha
}
return a , nil
}
func NewTGSReq (cname types .PrincipalName , kdcRealm string , c *config .Config , tgt Ticket , sessionKey types .EncryptionKey , sname types .PrincipalName , renewal bool ) (TGSReq , error ) {
a , err := tgsReq (cname , sname , kdcRealm , renewal , c )
if err != nil {
return a , err
}
err = a .setPAData (tgt , sessionKey )
return a , err
}
func NewUser2UserTGSReq (cname types .PrincipalName , kdcRealm string , c *config .Config , clientTGT Ticket , sessionKey types .EncryptionKey , sname types .PrincipalName , renewal bool , verifyingTGT Ticket ) (TGSReq , error ) {
a , err := tgsReq (cname , sname , kdcRealm , renewal , c )
if err != nil {
return a , err
}
a .ReqBody .AdditionalTickets = []Ticket {verifyingTGT }
types .SetFlag (&a .ReqBody .KDCOptions , flags .EncTktInSkey )
err = a .setPAData (clientTGT , sessionKey )
return a , err
}
func tgsReq(cname , sname types .PrincipalName , kdcRealm string , renewal bool , c *config .Config ) (TGSReq , error ) {
nonce , err := rand .Int (rand .Reader , big .NewInt (math .MaxInt32 ))
if err != nil {
return TGSReq {}, err
}
t := time .Now ().UTC ()
k := KDCReqFields {
PVNO : iana .PVNO ,
MsgType : msgtype .KRB_TGS_REQ ,
ReqBody : KDCReqBody {
KDCOptions : types .NewKrbFlags (),
Realm : kdcRealm ,
CName : cname ,
SName : sname ,
Till : t .Add (c .LibDefaults .TicketLifetime ),
Nonce : int (nonce .Int64 ()),
EType : c .LibDefaults .DefaultTGSEnctypeIDs ,
},
Renewal : renewal ,
}
if c .LibDefaults .Forwardable {
types .SetFlag (&k .ReqBody .KDCOptions , flags .Forwardable )
}
if c .LibDefaults .Canonicalize {
types .SetFlag (&k .ReqBody .KDCOptions , flags .Canonicalize )
}
if c .LibDefaults .Proxiable {
types .SetFlag (&k .ReqBody .KDCOptions , flags .Proxiable )
}
if c .LibDefaults .RenewLifetime > time .Duration (0 ) {
types .SetFlag (&k .ReqBody .KDCOptions , flags .Renewable )
k .ReqBody .RTime = t .Add (c .LibDefaults .RenewLifetime )
}
if !c .LibDefaults .NoAddresses {
ha , err := types .LocalHostAddresses ()
if err != nil {
return TGSReq {}, fmt .Errorf ("could not get local addresses: %v" , err )
}
ha = append (ha , types .HostAddressesFromNetIPs (c .LibDefaults .ExtraAddresses )...)
k .ReqBody .Addresses = ha
}
if renewal {
types .SetFlag (&k .ReqBody .KDCOptions , flags .Renew )
types .SetFlag (&k .ReqBody .KDCOptions , flags .Renewable )
}
return TGSReq {
k ,
}, nil
}
func (k *TGSReq ) setPAData (tgt Ticket , sessionKey types .EncryptionKey ) error {
b , err := k .ReqBody .Marshal ()
if err != nil {
return krberror .Errorf (err , krberror .EncodingError , "error marshaling TGS_REQ body" )
}
etype , err := crypto .GetEtype (sessionKey .KeyType )
if err != nil {
return krberror .Errorf (err , krberror .EncryptingError , "error getting etype to encrypt authenticator" )
}
cb , err := etype .GetChecksumHash (sessionKey .KeyValue , b , keyusage .TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM )
if err != nil {
return krberror .Errorf (err , krberror .ChksumError , "error getting etype checksum hash" )
}
auth , err := types .NewAuthenticator (tgt .Realm , k .ReqBody .CName )
if err != nil {
return krberror .Errorf (err , krberror .KRBMsgError , "error generating new authenticator" )
}
auth .Cksum = types .Checksum {
CksumType : etype .GetHashID (),
Checksum : cb ,
}
apReq , err := NewAPReq (tgt , sessionKey , auth )
if err != nil {
return krberror .Errorf (err , krberror .KRBMsgError , "error generating new AP_REQ" )
}
apb , err := apReq .Marshal ()
if err != nil {
return krberror .Errorf (err , krberror .EncodingError , "error marshaling AP_REQ for pre-authentication data" )
}
k .PAData = types .PADataSequence {
types .PAData {
PADataType : patype .PA_TGS_REQ ,
PADataValue : apb ,
},
}
return nil
}
func (k *ASReq ) Unmarshal (b []byte ) error {
var m marshalKDCReq
_ , err := asn1 .UnmarshalWithParams (b , &m , fmt .Sprintf ("application,explicit,tag:%v" , asnAppTag .ASREQ ))
if err != nil {
return krberror .Errorf (err , krberror .EncodingError , "error unmarshaling AS_REQ" )
}
expectedMsgType := msgtype .KRB_AS_REQ
if m .MsgType != expectedMsgType {
return krberror .NewErrorf (krberror .KRBMsgError , "message ID does not indicate a AS_REQ. Expected: %v; Actual: %v" , expectedMsgType , m .MsgType )
}
var reqb KDCReqBody
err = reqb .Unmarshal (m .ReqBody .Bytes )
if err != nil {
return krberror .Errorf (err , krberror .EncodingError , "error processing AS_REQ body" )
}
k .MsgType = m .MsgType
k .PAData = m .PAData
k .PVNO = m .PVNO
k .ReqBody = reqb
return nil
}
func (k *TGSReq ) Unmarshal (b []byte ) error {
var m marshalKDCReq
_ , err := asn1 .UnmarshalWithParams (b , &m , fmt .Sprintf ("application,explicit,tag:%v" , asnAppTag .TGSREQ ))
if err != nil {
return krberror .Errorf (err , krberror .EncodingError , "error unmarshaling TGS_REQ" )
}
expectedMsgType := msgtype .KRB_TGS_REQ
if m .MsgType != expectedMsgType {
return krberror .NewErrorf (krberror .KRBMsgError , "message ID does not indicate a TGS_REQ. Expected: %v; Actual: %v" , expectedMsgType , m .MsgType )
}
var reqb KDCReqBody
err = reqb .Unmarshal (m .ReqBody .Bytes )
if err != nil {
return krberror .Errorf (err , krberror .EncodingError , "error processing TGS_REQ body" )
}
k .MsgType = m .MsgType
k .PAData = m .PAData
k .PVNO = m .PVNO
k .ReqBody = reqb
return nil
}
func (k *KDCReqBody ) Unmarshal (b []byte ) error {
var m marshalKDCReqBody
_ , err := asn1 .Unmarshal (b , &m )
if err != nil {
return krberror .Errorf (err , krberror .EncodingError , "error unmarshaling KDC_REQ body" )
}
k .KDCOptions = m .KDCOptions
if len (k .KDCOptions .Bytes ) < 4 {
tb := make ([]byte , 4 -len (k .KDCOptions .Bytes ))
k .KDCOptions .Bytes = append (tb , k .KDCOptions .Bytes ...)
k .KDCOptions .BitLength = len (k .KDCOptions .Bytes ) * 8
}
k .CName = m .CName
k .Realm = m .Realm
k .SName = m .SName
k .From = m .From
k .Till = m .Till
k .RTime = m .RTime
k .Nonce = m .Nonce
k .EType = m .EType
k .Addresses = m .Addresses
k .EncAuthData = m .EncAuthData
if len (m .AdditionalTickets .Bytes ) > 0 {
k .AdditionalTickets , err = unmarshalTicketsSequence (m .AdditionalTickets )
if err != nil {
return krberror .Errorf (err , krberror .EncodingError , "error unmarshaling additional tickets" )
}
}
return nil
}
func (k *ASReq ) Marshal () ([]byte , error ) {
m := marshalKDCReq {
PVNO : k .PVNO ,
MsgType : k .MsgType ,
PAData : k .PAData ,
}
b , err := k .ReqBody .Marshal ()
if err != nil {
var mk []byte
return mk , err
}
m .ReqBody = asn1 .RawValue {
Class : asn1 .ClassContextSpecific ,
IsCompound : true ,
Tag : 4 ,
Bytes : b ,
}
mk , err := asn1 .Marshal (m )
if err != nil {
return mk , krberror .Errorf (err , krberror .EncodingError , "error marshaling AS_REQ" )
}
mk = asn1tools .AddASNAppTag (mk , asnAppTag .ASREQ )
return mk , nil
}
func (k *TGSReq ) Marshal () ([]byte , error ) {
m := marshalKDCReq {
PVNO : k .PVNO ,
MsgType : k .MsgType ,
PAData : k .PAData ,
}
b , err := k .ReqBody .Marshal ()
if err != nil {
var mk []byte
return mk , err
}
m .ReqBody = asn1 .RawValue {
Class : asn1 .ClassContextSpecific ,
IsCompound : true ,
Tag : 4 ,
Bytes : b ,
}
mk , err := asn1 .Marshal (m )
if err != nil {
return mk , krberror .Errorf (err , krberror .EncodingError , "error marshaling AS_REQ" )
}
mk = asn1tools .AddASNAppTag (mk , asnAppTag .TGSREQ )
return mk , nil
}
func (k *KDCReqBody ) Marshal () ([]byte , error ) {
var b []byte
m := marshalKDCReqBody {
KDCOptions : k .KDCOptions ,
CName : k .CName ,
Realm : k .Realm ,
SName : k .SName ,
From : k .From ,
Till : k .Till ,
RTime : k .RTime ,
Nonce : k .Nonce ,
EType : k .EType ,
Addresses : k .Addresses ,
EncAuthData : k .EncAuthData ,
}
rawtkts , err := MarshalTicketSequence (k .AdditionalTickets )
if err != nil {
return b , krberror .Errorf (err , krberror .EncodingError , "error in marshaling KDC request body additional tickets" )
}
rawtkts .Tag = 11
if len (rawtkts .Bytes ) > 0 {
m .AdditionalTickets = rawtkts
}
b , err = asn1 .Marshal (m )
if err != nil {
return b , krberror .Errorf (err , krberror .EncodingError , "error in marshaling KDC request body" )
}
return b , nil
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .